package com.yita.demo04_project_reflect.dao.Impl.base;

import com.sun.xml.internal.ws.message.stream.PayloadStreamReaderMessage;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class BaseDAO<T> {
    protected Connection conn;
    protected PreparedStatement psmt;
    protected ResultSet rs;
    final String DRIVER = "com.mysql.cj.jdbc.Driver";
    final String URL = "jdbc:mysql://localhost:13306/fruitdb?useUnicode=true&characterEncoding=utf-8&useSSL=false";
    final String USER = "root";
    final String PWD = "root";

    private Class entityClass;

    public BaseDAO() {
        //这是一个抽象类,子类先调用父类的构造方法,故this是子类
        //!TODO 看看能否通过asm的方式直接在子类中获取泛型的类型
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        Type[] actualTypeArguments = genericSuperclass.getActualTypeArguments();


        try {
            entityClass = Class.forName(actualTypeArguments[0].getTypeName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //为preparedStatement对象设置参数,即设置sql参数
    private void setParams(PreparedStatement psmt, Object... params) {
        if (params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                try {
                    psmt.setObject(i + 1, params[i]);
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }

    }

    //通过反射为对象实例obj对象设置属性的值
    private void setObject(T obj, String name, Object value) {
        try {
            if (obj != null) {
                Class<?> tClass = obj.getClass();
                Field field = tClass.getDeclaredField(name);
                //如果没有找到这个属性会直接抛异常
                field.setAccessible(true);
                field.set(obj, value);
//              直接设置属性更通用一些,下面是通过方法设置属性
//                String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
//                Method declaredMethod = tClass.getDeclaredMethod(methodName, value.getClass());
//                declaredMethod.invoke(obj, value);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    protected Connection getConn() {
        try {
            Class.forName(DRIVER);
            return DriverManager.getConnection(URL, USER, PWD);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected void close(PreparedStatement psmt, ResultSet rs, Connection conn) {
        try {
            if (psmt != null) {
                psmt.close();
            }
            if (rs != null) {
                rs.close();
            }
            if (conn != null) {
                conn.close();
            }

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

    }
    //如果是删改返回行数,如果是增,返回主键自增的值
    public int executeUpdate(String sql, Object... params) {
        try {
            boolean flag = sql.trim().toUpperCase().startsWith("INSERT");
            conn = getConn();
            if (flag) {
                psmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            } else {
                psmt = conn.prepareStatement(sql);
            }

            setParams(psmt, params);
            int count = psmt.executeUpdate();
            if (flag) {
                rs = psmt.getGeneratedKeys();
                if (rs.next()) {
                    return ((Long)rs.getLong(1)).intValue();
                }
            }
            return count;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            close(psmt, rs, conn);
        }
        return 0;

    }

    public List<T> executeQuery(String sql, Object... params) {
        ArrayList<T> arr = new ArrayList<>();
        try {
            conn = getConn();
            psmt = conn.prepareStatement(sql);

            setParams(psmt, params);

            rs = psmt.executeQuery();

            //ResultSetMetaData 用于描述结果集的对象
            ResultSetMetaData metaData = rs.getMetaData();

            int columnCount = metaData.getColumnCount();

            while (rs.next()) {
                T obj = (T) entityClass.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    String name = metaData.getColumnName(i + 1);
                    Object value = rs.getObject(i + 1);
                    setObject(obj, name, value);
                }
                arr.add(obj);
            }

        } catch (SQLException | InstantiationException | IllegalAccessException throwables) {
            throwables.printStackTrace();
        }
        return arr;
    }

    //执行复制查询
    public List<Map<String,Object>> executeComplexQuery(String sql, Object... params) {
        ArrayList<Map<String, Object>> maps = new ArrayList<>();
        try {
            conn = getConn();
            psmt = conn.prepareStatement(sql);
            setParams(psmt, params);
            rs=psmt.executeQuery();

            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            while (rs.next()) {
                HashMap<String, Object> map = new HashMap<>();
                for (int i = 0; i < columnCount; i++) {
                    map.put(metaData.getColumnName(i + 1), rs.getObject(i + 1));
                }
                maps.add(map);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return maps;
    }

}
